#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

#include <time.h>

#include <opencv2/opencv.hpp>
#include <opencv/highgui.h>

int initConnection(char* host, int portno);
int initCamera(void);
int startSending(void);
void signal_handler(int sig);
void printUsage(void);

CvCapture *capture;
int sockfd;
bool inter = false;

int main(int argc, char *argv[]){

  if (argc != 3){
    printUsage();
    return 1;
  }

  int portno = atoi(argv[2]);
  char* host = argv[1];

  //Register signal handler
  if(signal(SIGINT, signal_handler) == SIG_ERR){
    fprintf(stderr, "Unable to bind handler.\n");
    return 1;
  }

  while(!inter){
    if(initConnection(host, portno) != 0 || inter){
      fprintf(stderr, "Unable to init connection.\n");
      return 1;
    }

    printf("Init Connection complete.\n");

    if(initCamera() != 0 || inter){
      fprintf(stderr, "Unable to init camera.\n");
    }

    printf("Init Camera complete.\n");
    printf("Starting sending.\n");

    startSending();
  }

  
  return 0;
}

int initConnection(char* host, int portno){
  struct sockaddr_in serv_addr;
  struct hostent *server;

  sockfd = socket(AF_INET, SOCK_STREAM, 0);
  if (sockfd < 0){
    fprintf(stderr, "Unable to open socket.\n");
    return 1;
  }

  //Perform host lookup
  server = gethostbyname(host);
  if (server == NULL){
    fprintf(stderr, "Unable to perform host lookup.\n");
    return 1;
  }

  //Setup the serv_addr structure
  bzero((char*) &serv_addr, sizeof(serv_addr));
  serv_addr.sin_family = AF_INET;
  bcopy((char*) server->h_addr,
	(char*) &serv_addr.sin_addr.s_addr,
	server->h_length);
  serv_addr.sin_port = htons(portno);

  //Attempt to connect
  while (connect(sockfd, (struct sockaddr *)&serv_addr,
	      sizeof(serv_addr)) < 0 && !inter){
    fprintf(stderr, "Unable to connect to given host and port.\n");
    fprintf(stderr, "Attempting reconnect in 5 second.\n");
    sleep(5);
  }

  //Connection sucessful
  return 0;
}

int initCamera(){
  
  //Init camera
  //Currently assume video0
  capture = cvCaptureFromCAM(0);
  if(!capture) {
    fprintf(stderr, "Unable to init webcam.\n");
    return 1;
  }

  //Init camera succesful
  return 0;
}

int startSending(){

  while(!inter){
    //Grab into a Mat
    cv::Mat frame;
    frame = cvQueryFrame(capture);

    /*if(!frame){
      fprintf(stderr, "Unable to grab image from camera.\n");
      break;
      }*/

    //Move to buf to encode to jpg
    cv::vector<uchar> buf;
    if(!imencode("*.jpg", frame, buf)){
      fprintf(stderr, "Unable to encode image.\n");
      break;
    }

    //Send size of incoming image
    int size = (int)buf.size();
    write(sockfd, &size, sizeof(int));

    //Change to char array to send
    char* message = new char[buf.size()];
    for(int i = 0; i < size; i++){
      message[i] = (char)buf[i];
    }

    //Send the char array
    int bytes_sent = 0;
    while(bytes_sent < size){
      int increment = write(sockfd, message+bytes_sent,
			    size-bytes_sent);
      if(increment == -1){
	printf("Incremetn is -1.\n");
	continue; //????
      }
      bytes_sent += increment;
    }
    delete []message;
    
    //Housekeeping
    fflush(stdout);
  }

  cvReleaseCapture(&capture);
  close(sockfd);
  return 0;
}

void signal_handler(int sig){
  printf("Interrupt Caught.\n");
  inter = true;
}

void printUsage(){
  printf("Usage: camera <host> <port>\n");
  return;
}
